package org.datascooter.bundle.attribute;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import org.datascooter.exception.EntityNotMappedException;
import org.datascooter.impl.DataSnip;
import org.datascooter.utils.LangUtils;
import org.datascooter.utils.SnipUtils;

/**
 * Purposed for making lazy loading of inner classes
 * 
 * @author nemo
 * 
 */
public class Reference extends AbstractAttribute {

	protected Method getter;
	protected Method setter;
	private DataSnip dataSnip;
	private ReferenceType type = ReferenceType.FIRST;
	private String keyAttribute;

	public Reference(String name) {
		super(name);
	}

	public Reference(String name, ReferenceType type, DataSnip copy) {
		super(name);
		this.type = type;
		dataSnip = copy;
	}
	public static enum ReferenceType {
		FIRST, LIST, SET, MAP;
	}

	@Override
	public String getActorName() {
		return getter.getName();
	}

	@Override
	public void explore(Class<?> clazz) throws SecurityException, NoSuchMethodException {
		if (name.startsWith(GET)) {
			getter = clazz.getMethod(name);
		} else {
			getter = clazz.getMethod(GET + LangUtils.capitalize(name));
		}
		setGetClass(getter.getReturnType());
	}

	@SuppressWarnings("unchecked")
	public <T> T getValue(Object obj) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
		return (T) getter.invoke(obj);
	}

	@Override
	public void setValue(Object obj, Object value) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
		setter.invoke(obj, value);
	}

	@SuppressWarnings("unchecked")
	@Override
	public Reference copy() {
		return new Reference(name, type, dataSnip);
	}

	public void setDataSnip(DataSnip dataSnip) {
		this.dataSnip = dataSnip;
	}

	public void setList(DataSnip dataSnip) {
		setDataSnip(dataSnip);
		type = ReferenceType.LIST;
	}

	public void setSet(DataSnip dataSnip) {
		setDataSnip(dataSnip);
		type = ReferenceType.SET;
	}

	public void setMap(DataSnip dataSnip, String keyAttribute) {
		this.keyAttribute = keyAttribute;
		setDataSnip(dataSnip);
		type = ReferenceType.MAP;
	}

	public DataSnip getDataSnip() {
		return dataSnip;
	}

	public ReferenceType getType() {
		return type;
	}

	public String getKeyAttribute() {
		return keyAttribute;
	}

	public void hookValue(Object... sourceObject) throws IllegalArgumentException, EntityNotMappedException, IllegalAccessException, InvocationTargetException, SecurityException, NoSuchMethodException {
		SnipUtils.hookValue(dataSnip, sourceObject);
	}
}
